package com.cucn.ingmall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cucn.common.constant.order.OrderAliPayNotifyConstant;
import com.cucn.common.constant.order.OrderRabbitMqConstant;
import com.cucn.common.constant.order.OrderStatusEnum;
import com.cucn.common.exception.NoStockException;
import com.cucn.common.to.SkuHasStockVO;
import com.cucn.common.to.dto.MemberResponseDTO;
import com.cucn.common.to.mq.OrderTO;
import com.cucn.common.to.mq.SeckillOrderTO;
import com.cucn.common.utils.PageUtils;
import com.cucn.common.utils.Query;
import com.cucn.common.utils.R;
import com.cucn.ingmall.order.constant.OrderConstant;
import com.cucn.ingmall.order.dao.OrderDao;
import com.cucn.ingmall.order.entity.OrderEntity;
import com.cucn.ingmall.order.entity.OrderItemEntity;
import com.cucn.ingmall.order.entity.PaymentInfoEntity;
import com.cucn.ingmall.order.feign.CartFeignService;
import com.cucn.ingmall.order.feign.MemberFeignService;
import com.cucn.ingmall.order.feign.ProductFeignService;
import com.cucn.ingmall.order.feign.WareFeignService;
import com.cucn.ingmall.order.interceptor.LoginUserInterceptor;
import com.cucn.ingmall.order.service.OrderItemService;
import com.cucn.ingmall.order.service.OrderService;
import com.cucn.ingmall.order.service.PaymentInfoService;
import com.cucn.ingmall.order.to.OrderCreateTO;
import com.cucn.ingmall.order.to.WareSkuLockDTO;
import com.cucn.ingmall.order.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Resource
    private MemberFeignService memberFeignService;
    @Resource
    private CartFeignService cartFeignService;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource
    private WareFeignService wareFeignService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private ProductFeignService productFeignService;
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private OrderDao orderDao;
    @Resource
    private PaymentInfoService paymentInfoService;
    @Resource
    private RabbitTemplate rabbitTemplate;

    private ThreadLocal<OrderSubmitVO> orderSubmitVOThreadLocal = new ThreadLocal<>();

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 订单确认页返回需要用的数据
     *
     * @return 订单确认页数据
     */
    @Override
    public OrderConfirmVO confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVO confirmVO = new OrderConfirmVO();
        //获取当前的登录用户信息
        MemberResponseDTO member = LoginUserInterceptor.loginUser.get();

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        //1.用户收货地址
        CompletableFuture<Void> memberAddressFuture = CompletableFuture.runAsync(() -> {
            //子线程的内部手动共享请求上下文
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //远程调用用户服务查询用户收货地址
            R rMemberAddress = memberFeignService.getAddressByMemberId(member.getId());
            List<MemberAddressVO> memberAddressList = rMemberAddress.getData(new TypeReference<List<MemberAddressVO>>() {
            });
            confirmVO.setAddress(memberAddressList);
        }, threadPoolExecutor);


        //2.远程查询购物车中所有选中的购物项
        CompletableFuture<Void> orderItemsFuture = CompletableFuture.runAsync(() -> {
                    //子线程的内部手动共享请求上下文
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    R rCartItemCheck = cartFeignService.currentUserCartItems();
                    List<OrderItemVO> items = rCartItemCheck.getData(new TypeReference<List<OrderItemVO>>() {
                    });
                    confirmVO.setItems(items);
                }, threadPoolExecutor)
                .thenRunAsync(() -> {
                    //远程调用查询商品是否有库存?
                    List<OrderItemVO> items = confirmVO.getItems();
                    List<Long> skuIds = items.stream().map(OrderItemVO::getId).collect(Collectors.toList());
                    R rSkusHasStock = wareFeignService.getSkusHasStock(skuIds);
                    List<SkuHasStockVO> rSkusHasStockData = rSkusHasStock.getData(new TypeReference<List<SkuHasStockVO>>() {
                    });
                    Map<Long, Boolean> stocks = rSkusHasStockData.stream().collect(Collectors.toMap(SkuHasStockVO::getSkuId, SkuHasStockVO::getHasStock));
                    confirmVO.setStocks(stocks);
                }, threadPoolExecutor);

        //3.查询用户积分
        confirmVO.setIntegration(member.getIntegration());

        //防重令牌,保证接口幂等性
        String token = UUID.randomUUID().toString().replace("-", "");
        stringRedisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId(), token, 30, TimeUnit.SECONDS);
        confirmVO.setOrderToken(token);

        //等待异步任务完成
        CompletableFuture.allOf(memberAddressFuture, orderItemsFuture).get();

        //4.其他数据自动计算

        return confirmVO;
    }

    /**
     * 提交订单
     * 创建订单,验令牌,验价格,锁库存
     *
     * @param vo 订单提交的信息
     * @return 生成的订单信息
     */
    @Override
    @Transactional
    public SubmitOrderResponseVO submitOrder(OrderSubmitVO vo) {
        SubmitOrderResponseVO submitOrderResponseVO = new SubmitOrderResponseVO();
        submitOrderResponseVO.setCode(OrderConstant.SUBMIT_ORDER_SUCCESS);
        orderSubmitVOThreadLocal.set(vo);
        //获取当前的登录用户信息
        MemberResponseDTO member = LoginUserInterceptor.loginUser.get();

        //验证令牌,对比与删除必须保证原子性
        //返回值  0-校验失败  1-成功
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        String orderToken = vo.getOrderToken();
        Long scriptResult = stringRedisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(OrderConstant.USER_ORDER_TOKEN_PREFIX + member.getId()), orderToken);
        if (scriptResult == 0L) {
            //校验失败
            submitOrderResponseVO.setCode(OrderConstant.CHECK_CODE_ERROR);
            return submitOrderResponseVO;
        } else {
            //校验成功
            //下单的其他操作,验价格,锁库存,创建订单
            //1.创建订单
            OrderCreateTO order = createOrder();

            //2.验价
            BigDecimal payAmount = order.getOrder().getPayAmount();
            BigDecimal payPrice = vo.getPayPrice();
            //价格差距小于0.01
            if (Math.abs(payAmount.subtract(payPrice).doubleValue()) < 0.01) {
                //金额对比成功

                //3.保存订单
                saveOrder(order);

                //4.保存订单数据成功后，要锁定库存，如果库存锁定失败还要回滚
                //订单号,所有订单项信息(skuId,skuNum,skuName)
                WareSkuLockDTO wareSkuLockDTO = new WareSkuLockDTO();
                wareSkuLockDTO.setOrderSn(order.getOrder().getOrderSn());
                List<OrderItemVO> locks = order.getOrderItems().stream().map(orderItemEntity -> {
                    OrderItemVO orderItemVO = new OrderItemVO();
                    orderItemVO.setId(orderItemEntity.getSkuId());
                    orderItemVO.setCount(orderItemEntity.getSkuQuantity());
                    orderItemVO.setTitle(orderItemEntity.getSkuName());
                    return orderItemVO;
                }).collect(Collectors.toList());
                wareSkuLockDTO.setLocks(locks);

                //远程调用锁定库存
                R rOrderLockSkuWare = wareFeignService.orderLockSkuWare(wareSkuLockDTO);
                if (rOrderLockSkuWare.getCode() == 0) {

                    //锁定库存成功
                    submitOrderResponseVO.setOrder(order.getOrder());
                    //int i = 10 / 0;
                    //KouTODO:2022/1/27 14:23 Kou 订单创建成功,发送消息给mq
                    rabbitTemplate.convertAndSend(OrderRabbitMqConstant.ORDER_EVENT_EXCHANGE, OrderRabbitMqConstant.ORDER_CREATE_ORDER_ROUTING_KEY, order.getOrder());

                    return submitOrderResponseVO;
                } else {
                    //锁定库存失败
                    submitOrderResponseVO.setCode(OrderConstant.LOCKED_INVENTORY_ERROR);
                    String msg = (String) rOrderLockSkuWare.get("msg");
                    throw new NoStockException(msg);
                }

            } else {
                //验价失败
                submitOrderResponseVO.setCode(OrderConstant.VERIFY_PRICE_ERROR);
                return submitOrderResponseVO;
            }
        }
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        OrderEntity order = this.getOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
        return order;
    }

    /**
     * 订单关单,消费创建时的死信消息
     *
     * @param order 订单
     */
    @Override
    public void closeOrder(OrderEntity order) {
        OrderEntity orderEntity = this.getById(order.getId());
        Integer orderStatus = orderEntity.getStatus();

        //更新订单
        if (orderStatus.equals(OrderStatusEnum.CREATE_NEW.getCode())) {
            log.info("开始关闭订单,订单号===>:{}", order.getOrderSn());
            OrderEntity orderUpdateById = new OrderEntity();
            orderUpdateById.setId(order.getId());
            orderUpdateById.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(orderUpdateById);

            OrderTO orderTO = new OrderTO();
            BeanUtils.copyProperties(orderEntity, orderTO);

            //关单后立即向库存服务发送消息
            //需要保证消息一定会发出去,每一个消息都做好日志记录,定期扫描数据库,将失败的消息再重新发送一遍
            rabbitTemplate.convertAndSend(OrderRabbitMqConstant.ORDER_EVENT_EXCHANGE, OrderRabbitMqConstant.ORDER_RELEASE_OTHER_ROUTING_KEY, orderTO);

            //KouTODO:2022/1/27 17:06 Kou 将没发送成功的消息重新发送
        }
    }

    /**
     * 获取当前订单的支付信息
     *
     * @param orderSn 订单号
     * @return
     */
    @Override
    public PayVo getOrderPay(String orderSn) {
        OrderEntity order = this.getOrderByOrderSn(orderSn);
        PayVo payVo = new PayVo();
        //查到一个订单项信息设置名字
        List<OrderItemEntity> orderItems = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn()));
        payVo.setSubject(orderItems.get(0).getSkuName());
        payVo.setBody(orderItems.get(0).getSkuAttrsVals());
        payVo.setOut_trade_no(order.getOrderSn());
        BigDecimal payAmount = order.getPayAmount().setScale(2, RoundingMode.UP);
        payVo.setTotal_amount(payAmount.toString());

        return payVo;
    }

    /**
     * 分页查询指定用户订单的所有数据
     *
     * @param params 分页相关参数
     * @return
     */
    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberResponseDTO member = LoginUserInterceptor.loginUser.get();

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                //查询的是已登录用户的订单
                new QueryWrapper<OrderEntity>().eq("member_id", member.getId()).orderByDesc("id")
        );

        //设置每个订单的订单项
        List<OrderEntity> orderRecords = page.getRecords();
        List<OrderEntity> orderPage = orderRecords.stream().peek(order -> {
            QueryWrapper<OrderItemEntity> wrapper = new QueryWrapper<OrderItemEntity>().eq("order_sn", order.getOrderSn());
            order.setOrderItemList(orderItemService.list(wrapper));
        }).collect(Collectors.toList());

        page.setRecords(orderPage);
        return new PageUtils(page);
    }

    /**
     * 处理支付宝的支付结果
     *
     * @param payAsyncVo
     * @return
     */
    @Override
    public String handlePayResult(PayAsyncVo payAsyncVo) {
        //保存交易流水
        PaymentInfoEntity paymentInfo = new PaymentInfoEntity();
        paymentInfo.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfo.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfo.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfo.setCreateTime(new Date());
        paymentInfo.setTotalAmount(new BigDecimal(payAsyncVo.getTotal_amount()));
        paymentInfo.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfoService.save(paymentInfo);

        //支付成功修改订单状态信息
        if (payAsyncVo.getTrade_status().equals(OrderAliPayNotifyConstant.TRADE_SUCCESS) || payAsyncVo.getTrade_status().equals(OrderAliPayNotifyConstant.TRADE_FINISHED)) {
            this.updateOrderStatus(paymentInfo.getOrderSn(), OrderStatusEnum.PAYED.getCode());
        }

        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTO seckillOrderTO) {
        //KouTODO:2022/2/22 14:38 Kou 保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(seckillOrderTO.getOrderSn());
        orderEntity.setMemberId(seckillOrderTO.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        BigDecimal payPrice = seckillOrderTO.getSeckillPrice().multiply(new BigDecimal(seckillOrderTO.getNum().toString()));
        orderEntity.setPayAmount(payPrice);
        this.save(orderEntity);

        //保存订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTO.getOrderSn());
        orderItemEntity.setRealAmount(payPrice);
        orderItemEntity.setSkuQuantity(seckillOrderTO.getNum());

        orderItemService.save(orderItemEntity);
    }

    /**
     * 修改订单状态
     */
    private void updateOrderStatus(String orderSn, Integer status) {
        orderDao.updateOrderStatus(orderSn, status);
    }

    /**
     * 保存订单数据
     *
     * @param order 创建的订单
     */
    private void saveOrder(OrderCreateTO order) {
        //保存订单
        OrderEntity orderEntity = order.getOrder();
        orderEntity.setModifyTime(new Date());
        orderEntity.setCreateTime(new Date());
        //KouTODO:2022/3/15 13:44 Kou 出错
//        orderDao.insertSelective(orderEntity);
        baseMapper.insert(orderEntity);

        //保存订单项
        List<OrderItemEntity> orderItemList = order.getOrderItems();
        orderItemService.saveBatch(orderItemList);
    }

    /**
     * 创建订单
     */
    private OrderCreateTO createOrder() {
        OrderCreateTO orderCreateTO = new OrderCreateTO();

        //生成订单号
        String orderSn = IdWorker.getTimeId();

        //生成订单
        OrderEntity order = buildOrder(orderSn);
        //订单项信息(购买商品的详细信息)
        List<OrderItemEntity> orderItemList = buildOrderItemList(orderSn);

        //价格相关信息
        computePrice(order, orderItemList);

        orderCreateTO.setOrder(order);
        orderCreateTO.setOrderItems(orderItemList);

        return orderCreateTO;
    }

    /**
     * 计算订单价格及其他一些相关信息
     *
     * @param order         订单
     * @param orderItemList 订单项
     */
    private void computePrice(OrderEntity order, List<OrderItemEntity> orderItemList) {
        //1.订单的总额,叠加每一个订单项的总额信息
        BigDecimal total = new BigDecimal("0");

        BigDecimal promotionAmount = new BigDecimal("0");
        BigDecimal couponAmount = new BigDecimal("0");
        BigDecimal integrationAmount = new BigDecimal("0");
        Integer integration = null;
        Integer giftGrowth = null;
        for (OrderItemEntity orderItem : orderItemList) {
            BigDecimal realAmount = orderItem.getRealAmount();
            total = total.add(realAmount);

            //订单项的促销,积分,优惠价格
            promotionAmount = promotionAmount.add(orderItem.getPromotionAmount());
            couponAmount = couponAmount.add(orderItem.getCouponAmount());
            integrationAmount = integrationAmount.add(orderItem.getIntegrationAmount());

            integration = orderItem.getGiftIntegration();
            giftGrowth = orderItem.getGiftGrowth();
        }
        order.setTotalAmount(total);

        //应付总额,订单总额+运费即可
        order.setPayAmount(total.add(order.getFreightAmount()));

        //订单的促销,积分,优惠价格
        order.setPromotionAmount(promotionAmount);
        order.setCouponAmount(promotionAmount);
        order.setIntegrationAmount(integrationAmount);

        //积分..成长值
        order.setIntegration(integration);
        order.setGrowth(giftGrowth);

    }

    /**
     * 构建订单方法
     *
     * @param orderSn 订单号
     */
    private OrderEntity buildOrder(String orderSn) {
        //订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        MemberResponseDTO memberResponseDTO = LoginUserInterceptor.loginUser.get();
        orderEntity.setMemberId(memberResponseDTO.getId());
        orderEntity.setMemberUsername(memberResponseDTO.getUsername());

        //收货地址信息,运费信息
        OrderSubmitVO orderSubmitVO = orderSubmitVOThreadLocal.get();
        R rAddressFare = wareFeignService.getFare(orderSubmitVO.getAddrId());
        FareVO addressFareData = rAddressFare.getData(new TypeReference<FareVO>() {
        });

        MemberAddressVO memberAddress = addressFareData.getAddress();
        orderEntity.setFreightAmount(addressFareData.getFare());
        orderEntity.setReceiverName(memberAddress.getName());
        orderEntity.setReceiverPhone(memberAddress.getPhone());
        orderEntity.setReceiverPostCode(memberAddress.getPostCode());
        orderEntity.setReceiverProvince(memberAddress.getProvince());
        orderEntity.setReceiverCity(memberAddress.getCity());
        orderEntity.setReceiverRegion(memberAddress.getRegion());
        orderEntity.setReceiverDetailAddress(memberAddress.getDetailAddress());

        //设置订单的相关状态信息
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setAutoConfirmDay(OrderConstant.AUTO_CONFIRM_DAY);

        //删除状态
        orderEntity.setDeleteStatus(0);

        return orderEntity;
    }

    /**
     * 构建所有订单项数据
     *
     * @param orderSn 订单号
     * @return 订单项的详细信息
     */
    private List<OrderItemEntity> buildOrderItemList(String orderSn) {

        R rUserCartItems = cartFeignService.currentUserCartItems();
        //购物车中的购物项信息,最后一次确定每个购物项价格
        List<OrderItemVO> cartItems = rUserCartItems.getData(new TypeReference<List<OrderItemVO>>() {
        });
        if (!CollectionUtils.isEmpty(cartItems)) {
            List<OrderItemEntity> orderItemEntityList = cartItems.stream().map(cartItem -> {
                //查询订单项内商品的详细信息
                OrderItemEntity orderItemEntity = buildOrderItem(cartItem);
                orderItemEntity.setOrderSn(orderSn);

                return orderItemEntity;
            }).collect(Collectors.toList());
            return orderItemEntityList;
        }

        return null;
    }

    /**
     * 构建某一个订单项
     *
     * @param cartItem 购物项
     * @return 某一个订单项详细信息
     */
    private OrderItemEntity buildOrderItem(OrderItemVO cartItem) {
        OrderItemEntity orderItem = new OrderItemEntity();
        //一个订单项分为如下信息
        //1.订单信息:订单号|buildOrderItemList已完成
        //2.商品spu信息,远程查询
        R rSpuInfo = productFeignService.getSpuInfoBySkuId(cartItem.getId());
        SpuInfoVO spuInfoData = rSpuInfo.getData(new TypeReference<SpuInfoVO>() {
        });
        orderItem.setSpuId(spuInfoData.getId());
        orderItem.setSpuName(spuInfoData.getSpuName());
        orderItem.setSpuBrand(spuInfoData.getBrandId().toString());
        orderItem.setCategoryId(spuInfoData.getCatalogId());

        //3.商品的sku信息
        orderItem.setSkuId(cartItem.getId());
        orderItem.setSkuName(cartItem.getTitle());
        orderItem.setSkuPic(cartItem.getDefaultImage());
        orderItem.setSkuQuantity(cartItem.getCount());
        orderItem.setSkuPrice(cartItem.getPrice());
        List<String> skuAttrList = cartItem.getSkuAttr();
        String skuAttr = StringUtils.collectionToDelimitedString(skuAttrList, ";");
        orderItem.setSkuAttrsVals(skuAttr);

        //4.优惠信息(忽略)
        //5.积分信息
        orderItem.setGiftGrowth(cartItem.getTotalPrice().intValue());
        orderItem.setGiftIntegration(cartItem.getTotalPrice().intValue());

        //6.订单项的价格信息
        orderItem.setPromotionAmount(new BigDecimal("0"));
        orderItem.setCouponAmount(new BigDecimal("0"));
        orderItem.setIntegrationAmount(new BigDecimal("0"));

        //当前订单项的实际金额
        BigDecimal orign = orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity().toString()));
        //总额减去各种优惠
        BigDecimal realAmount = orign.subtract(orderItem.getCouponAmount()).subtract(orderItem.getPromotionAmount()).subtract(orderItem.getIntegrationAmount());
        orderItem.setRealAmount(realAmount);

        return orderItem;
    }
}